<template>
    <div class="surface-preview">
        <canvas ref="canvas" />
        <UxLoading v-show="loading" />
    </div>
</template>

<script>
import { fabric as Fabric } from "fabric";
import DesignResource from '@/utils/DesignResource.js';
import DesignEditor from '@/utils/DesignEditor.js';

export default {
    props: {
        color: { require: false, type: Object, default: null },
        pieces: { require: true, type: Array, default () { return [] } },
        surface: { require: true, type: Object, default: null },
    },
    data () {
        return {
            fabric: null,
            mask: null,
            background: null,
            loading: false,
            cache: {}, // 缓存
        };
    },
    watch: {
        color () { this.init(); },
        surface () { this.init(); },
        pieces: {
            deep: true,
            handler () {
                const option = this.getOption();

                this.updateModelPieces(option).then(res => {
                    // 位置排序
                    this.updateModelIndex(option);
                    // 更新
                    this.fabric && this.fabric.renderAll();
                })
            }
        }
    },

    methods: {


        // 获取参数信息
        getOption () {

            const option = {
                scale: 1,
                surface_name: '',
                mask_image: '',
                outer_width: 2048,
                outer_height: 2048,

                ...this.surface,
                color: this.color,
                pieces: this.pieces || [],
            }

            if (this.$el) {
                const rect = this.$el.getBoundingClientRect();
                const scale = 2 * Math.max(rect.width / option.outer_width, rect.height / option.outer_height);
                option.scale = scale;
            }

            return option;
        },

        // 更新容器
        updataFabric (option) {

            const width = option.scale * option.outer_width;
            const height = option.scale * option.outer_height;

            if (!this.fabric) {
                this.cache.scale = option.scale;
                this.fabric = new Fabric.StaticCanvas(this.$refs.canvas || null, {
                    enableRetinaScaling: false,
                    width: width,
                    height: height,
                });
            } else if (option.scale != this.cache.scale) {
                this.cache.scale = option.scale;
                this.fabric.setWidth(width);
                this.fabric.setHeight(height);
            } else {
                // 没发生变化
            }

            return Promise.resolve();
        },

        // 更新模特的所有设计面
        updateModelPieces (option) {

            const stages = this.fabric.getObjects();
            const piecesObject = Object.fromEntries(option.pieces.map(i => [i.canvas_name, i]));
            const stagesObject = Object.fromEntries(stages.map(i => [i.canvas_name, i]));
            const isused = ['background', 'mask'];
            const promises = [];

            const getStageDraw = (stage, mapper) => {


                return stage.editor.renderLayers().then(res => {

                    const canvas = stage.editor.fabric.lowerCanvasEl;

                    const stageOption = {
                        left: option.scale * (mapper.x + 0.5 * mapper.width),
                        top: option.scale * (mapper.y + 0.5 * mapper.height),
                        width: canvas.width,
                        height: canvas.height,
                        scaleX: option.scale * mapper.width / canvas.width,
                        scaleY: option.scale * mapper.height / canvas.height,
                        angle: mapper.rotate,
                        opacity: 0.95,
                        clipPath: null,
                    }

                    if (mapper.clip) {
                        stageOption.clipPath = new Fabric.Path(mapper.clip, {
                            absolutePositioned: true,
                            lockScalingX: true,
                            lockScalingY: true,
                            lockMovementX: true,
                            lockMovementY: true,
                            lockSkewingX: true,
                            lockSkewingY: true,
                            originX: 'left',
                            originY: 'top',
                            scaleX: option.scale,
                            scaleY: option.scale,
                            fill: '#f00',
                        })

                        stageOption.clipPath.set({
                            left: stageOption.clipPath.left * option.scale,
                            top: stageOption.clipPath.top * option.scale,
                        })
                    } else {
                        // 没有裁剪
                    }

                    stage.setElement(canvas);
                    stage.set(stageOption);
                })

            }

            option.mapping.map(mapper => {
                const piece = piecesObject[mapper.canvas_name];

                if (piece && piece.layers.length > 0) {
                    isused.push(mapper.canvas_name);


                    const editorOption = {
                        canvas_scale: option.scale,
                        canvas_model: "simple",
                        canvas_name: piece.canvas_name,
                        canvas_width: piece.canvas_width,
                        canvas_height: piece.canvas_height,
                        design_layers: piece.layers,
                    };

                    if (!stagesObject[piece.canvas_name]) {
                        // 没有初始化
                        const stage = new Fabric.Image(null, { originX: 'center', originY: 'center' });
                        this.fabric.add(stage);
                        stage.canvas_name = mapper.canvas_name;
                        stage.canvas_cache = JSON.stringify(editorOption);
                        stage.editor = new DesignEditor(Fabric, null, editorOption);
                        promises.push(getStageDraw(stage, mapper))

                    } else {
                        const stage = stagesObject[piece.canvas_name];
                        const cache = JSON.stringify(editorOption);

                        if (stage.canvas_cache != cache) {
                            stage.canvas_cache = cache;
                            stage.editor.reset(editorOption);
                            promises.push(getStageDraw(stage, mapper))
                        }
                    }

                } else {
                    // 空值不用管
                }
            })

            // 删除不要的部分
            stages.map(stage => {
                if (isused.indexOf(stage.canvas_name) < 0) {
                    this.fabric.remove(stage);
                }
            })

            return Promise.all(promises);

        },

        // 背景图
        updateModelBackground (option) {

            const name = (option.surface_name.match(/^\S+/g) || [])[0] || ''

            const color = option.color || {};
            const color_num = color.color_num || '';
            const color_url = color['bg_' + name.toLowerCase()];
            const color_able = color_num || color_url;
            const background = this.fabric.getObjects().filter(stage => stage.canvas_name == 'background')[0]

            const updateStage = (stage) => {

                if (stage.color_url) {

                    const opt = { type: 'simple', form: 'model' }
                    return DesignResource.loadImage(stage.color_url, opt).then(img => {
                        stage.setElement(img);
                        stage.set({
                            left: this.fabric.width / 2,
                            top: this.fabric.height / 2,
                            width: img.width,
                            height: img.height,
                            scaleX: this.fabric.width / img.width,
                            scaleY: this.fabric.height / img.height,
                            opacity: 1,
                            backgroundColor: '',
                        });
                    })

                } else {
                    stage.setElement(null);
                    stage.set({
                        left: this.fabric.width / 2,
                        top: this.fabric.height / 2,
                        width: this.fabric.width,
                        height: this.fabric.height,
                        scaleX: 1,
                        scaleY: 1,
                        opacity: 1,
                        backgroundColor: this.$modelBackgroundColor(stage.color_num, option),
                    });
                    return Promise.resolve();
                }

            }

            if (background && !color_able) {
                // 删除
                this.fabric.remove(stage);
                return Promise.resolve();
            } else if (background && color_able) {
                // 更新
                if (background.color_url == color_url && background.color_num == color_num) {
                    // 相同
                    return Promise.resolve();
                } else {
                    background.color_url = color_url;
                    background.color_num = color_num;
                    return updateStage(background);
                }

            } else if (!background && color_able) {
                // 添加
                const stage = new Fabric.Image(null, { originX: 'center', originY: 'center' })
                stage.canvas_name = 'background';
                stage.color_url = color_url;
                stage.color_num = color_num;
                this.fabric.add(stage);
                return updateStage(stage);
            } else {
                return Promise.resolve();
            }

        },

        // 更新遮罩图片
        updateModelMask (option) {

            const mask = this.fabric.getObjects().filter(stage => stage.canvas_name == 'mask')[0]
            const updateStage = (stage) => {
                const opt = { type: 'simple', form: 'model' }
                return DesignResource.loadImage(stage.mask_image, opt).then(img => {

                    stage.setElement(img);
                    stage.set({
                        left: this.fabric.width / 2,
                        top: this.fabric.height / 2,
                        width: img.width,
                        height: img.height,
                        scaleX: this.fabric.width / img.width,
                        scaleY: this.fabric.height / img.height,
                        opacity: 1,
                    })
                })
            }

            if (mask && !option.mask_image) {
                // 删除
            } else if (mask && option.mask_image && mask.mask_image != option.mask_image) {
                // 更新
                mask.mask_image = option.mask_image;
                return updateStage(mask);

            } else if (!mask && option.mask_image) {
                // 添加
                const stage = new Fabric.Image(null, { originX: 'center', originY: 'center' })
                stage.canvas_name = 'mask';
                stage.mask_image = option.mask_image;
                this.fabric.add(stage);
                return updateStage(stage);
            } else {
                // 相同
                return Promise.resolve();
            }
        },

        // 排序
        updateModelIndex (option) {

            const mapping = option.mapping || [];
            const stage = this.fabric.getObjects().slice(0);
            const names = ['background'].concat(mapping.map(item => item.canvas_name));

            // 设计置顶
            if (option.mapping_top) {
                names.splice(1, 0, 'mask')
            } else {
                names.push('mask');
            }
            // 排序
            stage.sort((a, b) => names.indexOf(a.canvas_name) - names.indexOf(b.canvas_name))
            stage.map((item, index) => this.fabric.moveTo(item, index));
        },

        // 初始化
        init () {
            const option = this.getOption();


            this.loading = true;

            // 更新初始化容器
            this.updataFabric(option).then(res => {
                return Promise.all([
                    this.updateModelMask(option),
                    this.updateModelBackground(option),
                    this.updateModelPieces(option),
                ])
            }).then(res => {
                // 位置排序
                this.updateModelIndex(option);
                // 更新
                this.fabric && this.fabric.renderAll();
                this.loading = false;
            }).catch(error => {
                this.loading = false;
                console.log(error);
            })
        },

    },
    mounted () {
        this.init();
    },
    // 销毁
    beforeDestroy () {
    }
};
</script>

<style lang='scss' scoped>
.surface-preview {
    position: relative;

    &::before {
        content: '';
        display: block;
        padding-bottom: 100%;
    }
    canvas {
        position: absolute;
        left: 0;
        top: 0;
        width: 100% !important;
        height: 100% !important;
    }
}
</style>
